package com.lagou.utils.helper;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 基于redis的分布式锁实现
 * 将时间戳设置为锁id
 */
@Component
@Slf4j
public class RedisLockHelper {

    @Qualifier(value = "redis")
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    // 锁过期时间 2分钟
    private static final long EXPIRE_TIME = 1000L << 1;

    /**
     * 分布式锁上锁
     *
     * @param lockId      锁id
     * @param currentTime 当前时间戳 锁的值
     * @return 是否拿到锁
     */
    public boolean lock(String lockId, String currentTime) {
        if (StringUtils.isEmpty(lockId) || StringUtils.isEmpty(currentTime)) {
            log.info("lockId:{}或 currentTime:{}为空", lockId, currentTime);
            return false;
        }
        String lock = redisTemplate.opsForValue().get(lockId);
        if (StringUtils.isBlank(lock)) {
            // 可以加锁
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockId, currentTime);
            // 能够顺利写入 则表示拿到锁
            if (isLock != null && isLock.equals(Boolean.TRUE)) {
                log.info("成功拿到锁 isLock:{}", isLock);
                return true;
            }
        }

        // 如果此时有两个线程并发 尝试拿到锁的值 或是发生死锁走此时的逻辑
        String oldValue = redisTemplate.opsForValue().get(lockId);
        if (StringUtils.isNotBlank(oldValue)) {
            // 当前时间戳减去之前的时间戳则表示锁过期
            boolean flag = Long.parseLong(currentTime) - Long.parseLong(oldValue) > EXPIRE_TIME;
            if (flag) {
                // 锁过期
                String newLock = redisTemplate.opsForValue().getAndSet(lockId, String.valueOf(System.currentTimeMillis()));
                log.info("锁已过期 并成功设置新的锁 newLock:{}", newLock);
                return StringUtils.isNotBlank(newLock);
            }
        }
        return false;
    }

    /**
     * 根据锁id删除分布式锁
     *
     * @param lockId 锁id
     */
    public void unlock(String lockId) {
        if (StringUtils.isEmpty(lockId)) {
            return;
        }
        try {
            String oldValue = redisTemplate.opsForValue().get(lockId);
            if (StringUtils.isBlank(oldValue)) {
                // 此锁不存在
                log.info("分布式锁:{} 不存在", oldValue);
                return;
            }
            Boolean isDelete = redisTemplate.delete(lockId);
            if (null != isDelete && isDelete.equals(Boolean.TRUE)) {
                log.info("分布式锁:{} 已释放", oldValue);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除分布式锁 指定值删除分布式锁
     *
     * @param lockId      锁id
     * @param currentTime 当前时间戳
     */
    public void unlock(String lockId, String currentTime) {
        if (StringUtils.isEmpty(lockId)) {
            return;
        }
        try {
            String oldValue = redisTemplate.opsForValue().get(lockId);
            if (!StringUtils.isBlank(oldValue) && StringUtils.equals(oldValue, currentTime)) {
                redisTemplate.delete(lockId);
                log.info(Thread.currentThread().getName() + "释放锁！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
